[アップデート] Amazon Cognito User pool の多要素認証に E メールを利用可能になったので AWS CDK で設定してみた

[アップデート] Amazon Cognito User pool の多要素認証に E メールを利用可能になったので AWS CDK で設定してみた

Clock Icon2024.11.10

こんにちは、製造ビジネステクノロジー部の若槻です。

最近の Amazon Cognito のアップデートで、User pool へのサインイン時の多要素認証に E メールを利用可能になりました。

https://dev.classmethod.jp/articles/cognito-mfa-email/

そしてこの E メールベースの多要素認証が AWS CDK でも v2.164.0 から L2 Construct で簡単に実装可能になりました。

cognito: support email based MFA (#31816) (f9d6eef), closes #31815

今回は、実際に Cognito user pool の E メールベースの多要素認証を AWS CDK で有効化してみました。

試してみた

CDK パッケージのアップデート

AWS CDK モジュールを v2.164.0 以上にアップデートします。

npm i aws-cdk-lib@latest aws-cdk@latest

メール送信元アドレスを SES の Identity として承認する

MFA としてメールを利用するためには、メール送信元アドレスを SES の Identity として承認する必要があります。今回だと後述の CDK コード中の noreply@${COGNITO_FROM_EMAIL_DOMAIN_NAME} のアドレスを承認します。

メールアドレスの承認も CDK で行うことができます。今回は以下記事を参考にして事前に実装しました。

https://dev.classmethod.jp/articles/adding-email-address-to-amazon-ses-identity-using-aws-cdk/

E メールベースの多要素認証を CDK で有効化する

Cognito user pool の E メールベースの多要素認証を AWS CDK で有効化するコードです。

lib/main-2-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as cognito from 'aws-cdk-lib/aws-cognito';
import { Construct } from 'constructs';

export class Main2Stack extends cdk.Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const region = this.region;

    const COGNITO_FROM_EMAIL_DOMAIN_NAME = process.env.DOMAIN_NAME || '';
    const COGNITO_DOMAIN_PREFIX = 'cm-test-20241110';
    const CALLBACK_URLS = ['https://dev.classmethod.jp/'];
    const LOGOUT_URLS = ['https://dev.classmethod.jp/'];
    const TEST_USER_EMAIL_ADDRESS = process.env.TEST_USER_EMAIL_ADDRESS || '';

    /**
     * Cognito User Pool
     */
    const userPool = new cognito.UserPool(this, 'UserPool', {
      selfSignUpEnabled: true,
      signInAliases: {
        email: true,
      },
      autoVerify: {
        email: true,
      },
      // 前提1: メールプロバイダーとして SES が設定されていること
      email: cognito.UserPoolEmail.withSES({
        sesRegion: region, // CDK 上ではオプションだが、実際には指定必須
        fromEmail: `noreply@${COGNITO_FROM_EMAIL_DOMAIN_NAME}`,
      }),
      mfa: cognito.Mfa.REQUIRED,
      mfaSecondFactor: {
        sms: true,
        otp: false,
        email: true, // e-mail による MFA の有効化
      },
      // 前提2: 高度なセキュリティ機能が有効化されていること
      advancedSecurityMode: cognito.AdvancedSecurityMode.ENFORCED,
      removalPolicy: cdk.RemovalPolicy.DESTROY, // 検証用リソースのため削除を許可
    });

    /**
     * Cognito User Pool Domain
     */
    userPool.addDomain('UserPoolDomain', {
      cognitoDomain: { domainPrefix: COGNITO_DOMAIN_PREFIX },
    });

    /**
     * Cognito User Pool Client
     */
    userPool.addClient('UserPoolClient', {
      generateSecret: false,
      oAuth: {
        callbackUrls: CALLBACK_URLS,
        logoutUrls: LOGOUT_URLS,
        flows: { authorizationCodeGrant: true },
      },
    });

    /**
     * Cognito User Pool User
     * 動作確認用のユーザーを作成
     */
    const userPoolUser = new cognito.CfnUserPoolUser(this, 'UserPoolUser', {
      userPoolId: userPool.userPoolId,
      username: TEST_USER_EMAIL_ADDRESS,
    });
    userPoolUser.node.addDependency(userPool);
  }
}

UserPool コンストラクトクラスmfaSecondFactoremail オプションが利用可能になっており、true を指定することで、メールによる MFA を有効化できます。

また E メールベースの多要素認証の有効化の前提として、メールプロバイダーとして SES が設定され、また高度なセキュリティ機能 (Advanced security features)が有効化されていることが必要なので、それらもコード中で設定しています。

上記実装を CDK Deploy によりデプロイします。

デプロイ後の設定の確認

デプロイにより構築された User pool の設定をマネジメントコンソールから確認してみます。

ユーザープールの高度なセキュリティ機能が有効化され、その中で Add an email option to MFA が有効化されています。

また、Sign-in experience で MFA methods として Email message が追加されています。

E メールによる MFA の動作確認

User pool の Hosted UI にアクセスし、ユーザー名とパスワードを入力してサインインを試みます。

するとコードがメールで送信されます。

このようなコードが記載されたメールが届くので、そのコードを入力します。

サインインが成功し、コールバック URL にリダイレクトされました。良さそうですね。

トラブルシュート

送信元メールアドレスが SES で未承認

CDK デプロイ時に次のようなエラーが発生する場合。

$  npm run deploy

> [email protected] deploy
> cdk deploy --require-approval never --method=direct


✨  Synthesis time: 5.41s

Main: deploying... [1/1]
Main: updating stack...
5:17:52 PM | UPDATE_FAILED        | AWS::Cognito::UserPool       | MyUserPoolD09D1D74
Resource handler returned message: "Cognito received the following error from Amazon SES when attempting to send email: Email address is not verified. The following identities failed the check in region AP-NORTHEAST-1: ar
n:aws:ses:ap-northeast-1:XXXXXXXXXXXX:identity/[email protected] (Service: CognitoIdentityProvider, Status Code: 400, Request ID: 7061cfbb-a6ff-4bb6-9382-b7fe47b22241)" (RequestToken: c57a9893-597f-de1
e-b300-54c21483eb48, HandlerErrorCode: InvalidRequest)

❌  Main failed: The stack named Main failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Cognito received the following error from Amazon SES when attempting to send email: Email address is not verified. The following identities failed the check in region AP-NORTHEAST-1: arn:aws:ses:ap-northeast-1:XXXXXXXXXXXX:identity/[email protected] (Service: CognitoIdentityProvider, Status Code: 400, Request ID: 7061cfbb-a6ff-4bb6-9382-b7fe47b22241)" (RequestToken: c57a9893-597f-de1e-b300-54c21483eb48, HandlerErrorCode: InvalidRequest)

送信元ドメインだけでなく、送信元メールアドレスも SES で承認されている必要があります。「メール送信元アドレスを SES の Identity として承認する」の対応がまだの場合は実施するようにします。

SMS 認証の有効化が求められる

CDK デプロイ時に次のようなエラーが発生する場合。

$ npm run deploy -- Main2

> [email protected] deploy
> cdk deploy --require-approval never --method=direct Main2


✨  Synthesis time: 5.55s

Main2: start: Building 92788324c5fdf7bdf48905d6c954c3eee5acc57ef34779f164579f917c87b6a0:current_account-current_region
Main2: success: Built 92788324c5fdf7bdf48905d6c954c3eee5acc57ef34779f164579f917c87b6a0:current_account-current_region
Main2: start: Publishing 92788324c5fdf7bdf48905d6c954c3eee5acc57ef34779f164579f917c87b6a0:current_account-current_region
Main2: success: Published 92788324c5fdf7bdf48905d6c954c3eee5acc57ef34779f164579f917c87b6a0:current_account-current_region
Main2: deploying... [1/1]
Main2: creating stack...
8:14:37 PM | CREATE_FAILED        | AWS::Cognito::UserPool       | MyUserPool
Resource handler returned message: "SMS configuration and Auto verification for phone_number are required when MFA is required/optional (Service: CognitoIdentityProvider, Status Code: 400,
Request ID: 342f39fa-f7e7-4da6-8673-9feb85b00a71)" (RequestToken: 6d2d35ec-4994-e991-df00-34eb306ed79c, HandlerErrorCode: InvalidRequest)

MFA 有効時は SMS 認証が有効化されている必要があります。そのため、CDK の実装が次のようになっていることを確認します。

      mfaSecondFactor: {
        sms: true, // 有効である必要がある
        otp: true,
        email: true,
      },

マネジメントコンソールから User pool の MFS を構成する際に下記のような注釈が表示されるので、MFA 有効化時は SMS 認証も有効化される必要があるようです。

This option must be selected because SMS is configured.

CDK 上では SMS 認証を無効化しても実際の設定では SMS 認証が有効化されている

前項は User pool 作成時のエラーですが、作成後に MFA を有効化した場合は SMS 認証を下記のように CDK 上で無効化することができます。

lib/main-2-stack.ts
      // 略
      mfaSecondFactor: {
        sms: false, // SMS 認証を無効化
        otp: false,
        email: true, // e-mail による MFA の有効化
      },
      // 略

しかしデプロイされた User pool の実際の MFA 設定では SMS 認証は有効化されたままとなっています。

エラーにならないため紛らわしいですが、作成後の User pool で有効化済みの MFA 設定ではこのような挙動となるようです。

参考

https://github.com/aws/aws-cdk/issues/11478

おわりに

今回は、Cognito user pool の E メールベースの多要素認証を AWS CDK で有効化する方法をご紹介しました。

E メールベースの多要素認証は、SMS に比べて利用者にとって利便性が高い場合が多いため、ユーザー体験の向上につながると思います。

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.